package com.iflytek.cosmo.ocr.common.core.worker;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import com.github.tobato.fastdfs.service.AppendFileStorageClient;
import com.iflytek.cosmo.ocr.common.core.BeanHelper;
import com.iflytek.cosmo.ocr.common.enums.ExportTaskStatusEnum;
import com.iflytek.cosmo.ocr.common.util.FileNameComparator;
import com.iflytek.cosmo.ocr.common.util.JSONUtil;
import com.iflytek.cosmo.ocr.common.util.UUIDUtil;
import com.iflytek.cosmo.ocr.dao.ExportTaskDao;
import com.iflytek.cosmo.ocr.model.domain.ExportFileInfo;
import com.iflytek.cosmo.ocr.model.entity.ExportTaskEntity;
import com.iflytek.cosmo.ocr.service.ExportTaskService;
import com.iflytek.cosmo.ocr.service.FileService;
import com.iflytek.icourt.common.constants.Constants;

import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;


/**
 * 压缩导出线程
 *
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @date 2019/12/3 10:10
 */
public class CompressFileExportWorker implements Runnable {
	
	private static final Logger logger = LoggerFactory.getLogger(CompressFileExportWorker.class);

    /** 导出任务 id */
    private String taskId;

    /** 待导出的文件 id 集合 */
    private List<String> fileIds;

    /** 需要导出的文件格式 */
    private String exportFormat;
    
    /** 导出用户Id */
    private String userId;
    
    /** 导出用户Id */
    private ExportTaskEntity task;
    
    private ExportTaskDao exportTaskDao;
    
    private ExportTaskService exportTaskService;
    
    private FileService fileService;
    
    private AppendFileStorageClient storageClient;
    
    
    @Value("${fdfs.url}")
    private String fdfsUrl;

    public CompressFileExportWorker(String taskId, List<String> fileIds, String exportFormat,String userId, ExportTaskEntity task) {
        this.taskId = taskId;
        this.fileIds = fileIds;
        this.exportFormat = exportFormat;
        this.userId=userId;
        this.task=task;
        this.exportTaskDao = BeanHelper.getExportTaskDao();
        this.fileService=BeanHelper.getFileServiceBean();
        this.exportTaskService=BeanHelper.getExportServiceBean();
        this.storageClient=BeanHelper.getStorageClient();
    }

    @Override
    public void run() {
    	try {
			String exportType=task.getExportType();
			//String fileName=task.getFileName()+"."+exportType.substring(exportType.lastIndexOf(".")+1,exportType.length());
			// 根据文件 id 和导出格式查询已经导出的所有子文件
			Example example = new Example(ExportTaskEntity.class);
			Criteria criteria = example.createCriteria();
			criteria.andIn("fileIds", fileIds);
			criteria.andEqualTo("exportType", exportFormat).andIsNotNull("filePath");
			List<ExportTaskEntity> tasks=exportTaskDao.selectByExample(example);
			//已存在记录需要去重
			tasks = tasks.stream().collect(//list是需要去重的list，返回值是去重后的list
	                Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ExportTaskEntity::getFileIds))),
	                        ArrayList::new));
			//存在导出记录的子文件集合
			List<String> existsTasks = tasks.stream().map(e -> e.getFileIds()).collect(Collectors.toList());
			// 过滤出未导出的子文件,
			List<String> nonExistTasks=null;
			if(CollectionUtils.isEmpty(existsTasks)) {
				nonExistTasks=fileIds;
			}else {
				nonExistTasks=fileIds.stream().filter(t-> !existsTasks.contains(t)).collect(Collectors.toList());
			}
			//如果全部都已经导出直接Return
			Example ex = new Example(ExportTaskEntity.class);
	        Criteria c = ex.createCriteria();
	        c.andEqualTo("id", taskId);
	        ExportTaskEntity taskEntity=exportTaskDao.selectOneByExample(ex);
			if(CollectionUtils.isEmpty(nonExistTasks) && null!=taskEntity && StringUtils.isNotEmpty(taskEntity.getFilePath())){
				return;
			}
			/*
			for 未导出过的子文件:
			    合成子文件
			    生成子文件到 fastDFS
			    新增子文件导出记录到 t_export_task 表

			    过程中有错误, 删除父任务(taskId), 方法结束(return)
			 */

			// 从 fastDFS 取出所有子文件
			//任务表中不存在记录集合
			List<ExportFileInfo> list1=this.exportToFile(nonExistTasks, exportFormat);
			//任务表中已经存在的记录
			List<ExportFileInfo> list2=new ArrayList<>();
			tasks.stream().forEach((task) ->{
				ExportFileInfo exportFileInfo=new ExportFileInfo();
				byte[] fileBytes = fileService.getFileBytes(task.getFilePath());
				exportFileInfo.setContent(fileBytes);
				exportFileInfo.setFileName(task.getFileName()+"."+task.getExportType());
				list2.add(exportFileInfo);
				
			});
			list1.addAll(list2);
			String folder=System.getProperty("java.io.tmpdir");
			// 合成 zip 文件
			//String zipFilePath=folder+File.separator+fileName;
			String zipFilePath=folder+File.separator+UUIDUtil.getUUID()+"."+exportType.substring(exportType.lastIndexOf(".")+1,exportType.length());
			logger.info("taskId ={},zipFilePath===={}",taskId,zipFilePath);
			File zipFile = new File(zipFilePath);
			//zip文件写入本地磁盘
			String zipPath=StringUtils.EMPTY;
			logger.info("taskId ={},list1.size===={}",taskId,list1.size());
			try (FileOutputStream fileOutputStream = new FileOutputStream(zipFile);
			        ZipOutputStream zippedOut = new ZipOutputStream(fileOutputStream)) {
				//对写入的zip文件按照windows规则排序
				Collections.sort(list1, new Comparator<ExportFileInfo>() {
		            private final Comparator<String> NATURAL_SORT = new FileNameComparator();
		            @Override
		            public int compare(ExportFileInfo ofe1, ExportFileInfo ofe2) {
		                String o1 = ofe1.getFileName();
		                String o2 = ofe2.getFileName();
		                return NATURAL_SORT.compare(o1, o2);
		            }
		        });
			    Integer writeSize = fileService.writeExportFileInfoTo(list1, zippedOut);
			    zippedOut.finish();
			    zippedOut.close();
			    fileOutputStream.close();
			    logger.info("taskId ={},writeSize===={}",taskId ,writeSize);
			    if (writeSize != null) {
			    	// 上传 zip 到 fastDFS
			    	byte[] bytes = this.getbyte(zipFilePath);
			    	logger.info("taskId ={},bytes.length()===={}",taskId ,bytes.length);
			    	zipPath=fileService.uploadFile(task.getFileName(), "zip", bytes);
			    }
			} catch (Exception e) {
			    logger.warn("写入zip文件异常. fileIds: {}，异常消息{}", JSONUtil.toStrByDefault(fileIds), e);
			    this.deleteTask(taskId);
			    
			}
			FileUtils.deleteQuietly(zipFile);
			//删除原来压缩文件
	        if(null!=taskEntity && StringUtils.isNotEmpty(taskEntity.getFilePath())) {
	        	String path=taskEntity.getFilePath();
                String groupName = path.substring(0, path.indexOf("/"));
                String attpath = path.substring(path.indexOf("/") + 1);
                storageClient.deleteFile(groupName, attpath);
	        }
	        
			// 更新任务完成, 保存 fastDFS 文件路径
			ExportTaskEntity entity=new ExportTaskEntity();
			entity.setId(taskId);
			entity.setStatus(ExportTaskStatusEnum.FINISHED.toString());
			entity.setFilePath(zipPath);
			entity.setUpdateTime(new Date());
			exportTaskDao.updateByPrimaryKeySelective(entity);
		} catch (Exception e) {
			logger.warn("批量导出文件异常. fileIds: {},异常消息：{}", JSONUtil.toStrByDefault(fileIds), e);
			this.deleteTask(taskId);
		}
    }
    /**
     * 合成子文件 上传fastDFS并入库
     * @param nonExistTasks
     * @param exportFormat
     */
    private List<ExportFileInfo> exportToFile(List<String> nonExistTasks, String exportFormat) {
		List<ExportFileInfo> list = new ArrayList<>();
		//生成子文件 上传fastDFS并入库
		for(String fileId:nonExistTasks) {
			try {
				ExportFileInfo fileInfo=null;
				if(exportFormat.equals(Constants.SupportExt.DOCX) || exportFormat.equalsIgnoreCase("wps")) {
					List<String>  orderFileIds=new ArrayList<>();
					orderFileIds.add(fileId);
					fileInfo= fileService.exportToFile(orderFileIds, exportFormat);
				}else {
					fileInfo= fileService.exportToFile(fileId, exportFormat);
				}
				if(null!=fileInfo && null!=fileInfo.getContent() && fileInfo.getContent().length >0) {
					String storePath=StringUtils.EMPTY;
					try {
						storePath = fileService.uploadFile(fileInfo.getFileName(), exportFormat, fileInfo.getContent());
					} catch (Exception e) {
						logger.info("文件{}！上传fastDFS异常，异常信息:{}",fileInfo.getFileName(),e);
					}
					if(StringUtils.isNotEmpty(storePath)) {
						list.add(fileInfo);
						ExportTaskEntity record=new ExportTaskEntity();
						record.setCreateTime(new Date());
						record.setExporterId(userId);
						record.setExportType(exportFormat);
						record.setFileIds(fileId);
						record.setFileName(fileInfo.getFileName().substring(0, fileInfo.getFileName().lastIndexOf(".")));
						record.setFilePath(storePath);
						record.setId(UUIDUtil.getUUID());
						record.setStatus(ExportTaskStatusEnum.FINISHED.toString());
						this.exportTaskDao.insertSelective(record);
					}
					
				}else {
					logger.info("文件{}！没有识别结果",fileInfo.getFileName());
				}
			} catch (Exception e) {
				logger.info("文件{}！导出文件异常，异常原因{}",fileId,e);
				continue;
			}
		}	
		return list;
    }
    
    /**
     * 获取zip文件字节数组
     * @param zipFilePath
     * @return
     */
    public byte[] getbyte(String zipFilePath) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        BufferedInputStream bis = null;
        FileInputStream in = null;
        try {
        	logger.info("zipFilePath=={}",zipFilePath);
            File zipfile = new File(zipFilePath);
            in = new FileInputStream(zipfile);
            bis = new BufferedInputStream(in);
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = bis.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
            bis.close();
            in.close();
            return out.toByteArray();
        } catch (Exception e) {
        	logger.info("遇到了异常,异常信息:{}",e);
            e.printStackTrace();
        } finally {
            try {
                if (bis != null)
                    bis.close();
                if (in != null)
                    in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
 
    }
    
    private boolean deleteTask(String taskId) {
	    exportTaskService.deleteByTaskId(taskId);
	    return true;
    }

}
